﻿using System;
using System.Linq;
using Consul;
using LeoGemini.Service.Common.Options;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp;
using Volo.Abp.Modularity;

namespace LeoGemini.Service.Common.Module
{
    public class LeoGeminiConsulModule : AbpModule
    {
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            var services = context.Services;
            var configuration = services.GetConfiguration();
            services.Configure<ServiceDiscoveryOptions>(configuration.GetSection("ServiceDiscovery"));
            services.AddConsul();
        }

        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            var app = context.GetApplicationBuilder();
            app.UseConsul();
        }
    }

    public static class ConsulExtensions
    {
        public static IServiceCollection AddConsul(this IServiceCollection services)
        {
            services.AddSingleton<IConsulClient, ConsulClient>(p => new ConsulClient(cfg =>
            {
                var options = p.GetRequiredService<IOptions<ServiceDiscoveryOptions>>().Value;
                cfg.Address = new Uri(options.Consul.HttpEndpoint);
            }));
            
            return services;
        }
        
        public static IApplicationBuilder UseConsul(this IApplicationBuilder app)
        {
            // 初始化当前service的唯一id
            var serviceId = Guid.NewGuid().ToString();
            
            // 获取lifetime
            var lifetime = app.ApplicationServices.GetRequiredService<IHostApplicationLifetime>();
            lifetime.ApplicationStarted.Register(() => RegisterToConsul(app, serviceId));
            lifetime.ApplicationStopping.Register(() => DeRegisterFromConsul(app, serviceId));
            
            return app;
        }

        public static void DeRegisterFromConsul(IApplicationBuilder app, string serviceId)
        {
            // 获取logger，记录配置流程信息
            var logger = app.ApplicationServices.GetRequiredService<ILoggerFactory>()
                .CreateLogger<ConsulClient>();
            // 获取consul客户端
            var client = app.ApplicationServices.GetRequiredService<IConsulClient>();
            logger.LogInformation($"向consul注册服务：ID {serviceId}");
            client.Agent.ServiceDeregister(serviceId).GetAwaiter().GetResult();
        }

        public static void RegisterToConsul(IApplicationBuilder app, string serviceId)
        {
            // 获取配置
            var options = app.ApplicationServices.GetRequiredService<IOptions<ServiceDiscoveryOptions>>().Value;
            var configuration = app.ApplicationServices.GetRequiredService<IConfiguration>();
            // 获取logger，记录配置流程信息
            var logger = app.ApplicationServices.GetRequiredService<ILoggerFactory>()
                .CreateLogger<ConsulClient>();
            // 获取consul客户端
            var client = app.ApplicationServices.GetRequiredService<IConsulClient>();
            
            var features = app.Properties["server.Features"] as FeatureCollection;
            var addresses = features?.Get<IServerAddressesFeature>()?.Addresses.Select(p => new Uri(p));

            foreach (var address in addresses)
            {
                var host = configuration.GetValue("SERVICE_DISCOVERY_HOST", address.Host);
                var port = configuration.GetValue("SERVICE_DISCOVERY_PORT", address.Port);
                logger.LogInformation($"向consul注册服务：ID {serviceId}，健康检查地址：http://{host}:{port}/health/check");
                var httpCheck = new AgentServiceCheck()
                {
                    DeregisterCriticalServiceAfter = TimeSpan.FromMinutes(1),
                    Interval = TimeSpan.FromSeconds(30),
                    HTTP = new Uri(
                        $"http://{host}:{port}/health/check"
                    ).OriginalString
                };
                var registration = new AgentServiceRegistration()
                {
                    Name = options.ServiceName,
                    ID = serviceId,
                    Address = host,
                    Port = port,
                    Checks = new []
                    {
                        httpCheck
                    }
                };
                client.Agent.ServiceRegister(registration).GetAwaiter().GetResult();
            }
        }
    }
}